#include "Output.h"
#include "Shapes.h"
#include "music.h"
#include "keyjoy.h"
#include "roll.h"
#include "chars.h"
#include "logo.h"

#include <conio.h>
#include <stdbool.h>
#include <stdio.h>
#include <peekpoke.h>
#include <6502.h>
#include <string.h>
#include <stdlib.h>
#include <c64.h>
#include <cbm.h>

#define DEFAULT_DELAY 100
static byte_t DELAY = DEFAULT_DELAY;
static byte_t LAST_DELAY = DEFAULT_DELAY;
static byte_t _countdown = DEFAULT_DELAY;
static byte_t _secondTimer = 60;
static bool _updateDisplay = false;
static bool _processAction = false;
static bool _moveDown = false;
static bool _fastDown = false;

static bool _turnDown = false;
static bool _leftDown = false;
static bool _rightDown = false;
static bool _endDown = false;
static bool _pauseDown = false;
static bool _muteDown = false;
static bool _mute = false;

static byte_t _actionCoolDown = 0;

static uint_t lines[4];
static uint_t _totalLines;

static bool _do_scroll = false;
static actions_e _action = A_NONE;
static bool _toggle = false;

byte_t _i;
static char _stack[256];

byte_t logo[];


#define FAST_DOWN 5;


bool checkLineFull(byte_t x, byte_t y, byte_t w)
{
	static byte_t i;

	for(i = 0; i < w; ++i)
	{
		if(!(codeat(x + i, y) != 32)) 
		{
			return false;
		}
	}

	return true;
}

uint_t checkLines(byte_t x, byte_t y, byte_t w, byte_t h)
{
	static byte_t i;
	static byte_t count;
	static uint_t result;

	count = 0;

	for(i = y + h; i >= y; --i)
	{
		if(checkLineFull(x, i, w))
		{
			++count;
			moveregion(x, y, w, i - y, x, y + 1);
			++i;
		}
	}

	if(count == 0) { return 0; }
	if(count == 1) { writestringxy(0, 23, "good"); ++lines[0]; result = 2 * w; }
	if(count == 2) { writestringxy(0, 23, "great!"); ++lines[1]; result = 5 * w; }
	if(count == 3) { writestringxy(0, 23, "excellant!"); ++lines[2]; result = 20 * w; }
	if(count == 4) { writestringxy(0, 23, "superb!!"); ++lines[3]; result = 80 * w; }

	_totalLines += count;

	LAST_DELAY = LAST_DELAY - count;

	if(LAST_DELAY < 13) 
	{
		LAST_DELAY = 13;
	}

	_countdown = DELAY = LAST_DELAY;

	return result;
}

void writeScore(unsigned long score)
{
	_fore_color = COLOR_GRAY1;
	clearxy(27, 2, 11);
	writenumberxy(37, 2, score);
}
unsigned char irq(void)
{
	--_secondTimer;

	if(_secondTimer == 0) 
	{
		_secondTimer = 60;
		srand(60 * _countdown);
	}

	if(_secondTimer % 20 == 0)
	{
		_processAction = true;
	}

	--_countdown;

	if(_countdown == 0) 
	{
		_moveDown = true;
		_updateDisplay = true;
		_countdown = DELAY;
	}

	if(!_mute) __asm__("JSR $1003");

	slowkey();
	joystick();

	if(scantable[7] == 128) _pauseDown = true;
	else if(scantable[0] == 16) _muteDown = true;
	else if(_actionCoolDown == 0 && (joy_fire == 1 || (scantable[0] == 128 && (scantable[1] == 128 || scantable[6] == 16)) || scantable[2] == 4  )) { _actionCoolDown= 10; _turnDown = true; } //_action = A_TURN;
	else if(_actionCoolDown == 0 && (joy_horizontal == 1 || (scantable[0] == 4   && (scantable[1] == 0   && scantable[6] == 0 )) || scantable[1] == 32 )) { _actionCoolDown= 10; _rightDown = true; } //_action = A_RIGHT;
	else if(_actionCoolDown == 0 && (joy_horizontal == -1 || (scantable[0] == 4   && (scantable[1] == 128 || scantable[6] == 16)) || scantable[1] == 4  )) { _actionCoolDown= 10; _leftDown = true; } //_action = A_LEFT;
	else if(scantable[0] == 8) _endDown = true;
	else if(scantable[0] != 0 || scantable[1] != 0 || scantable[2] != 0 || scantable[3] != 0 ||
		    scantable[4] != 0 || scantable[5] != 0 || scantable[6] != 0 || scantable[7] != 0) _action = A_ANY;

	if(_turnDown || _rightDown || _leftDown)
	{
		--_actionCoolDown;
	}

	if(_muteDown && scantable[0] != 16)
	{
		_muteDown = false;
		_mute = !_mute;
		if(_mute) 
		{
			POKE(0xD418, PEEK(0xD418) & 240);
		}
		else
		{
			POKE(0xD418, PEEK(0xD418) | 15);
		}
	}
	else if(_pauseDown && scantable[7] != 128)
	{
		_pauseDown = false;
		_action = A_PAUSE;
		_processAction = true;
		_actionCoolDown = 0;
	}
	else if(_turnDown && (_actionCoolDown == 0 || (joy_fire == 0 &&  !((scantable[0] == 128 && (scantable[1] == 128 || scantable[6] == 16)) || scantable[2] == 4  ))))
	{
		_turnDown = false;
		_action = A_TURN;
		_processAction = true;
		_actionCoolDown = 0;
	}
	else if(_rightDown && (_actionCoolDown == 0 || (joy_horizontal != 1 && !((scantable[0] == 4   && (scantable[1] == 0   && scantable[6] == 0 )) || scantable[1] == 32 ))))
	{
		_rightDown = false;
		_action = A_RIGHT;
		_processAction = true;
		_actionCoolDown = 0;
	}
	else if(_leftDown && (_actionCoolDown == 0 || (joy_horizontal != -1 && !((scantable[0] == 4   && (scantable[1] == 128 || scantable[6] == 16)) || scantable[1] == 4  ))))
	{
		_leftDown = false;
		_action = A_LEFT;
		_processAction = true;
		_actionCoolDown = 0;
	}
	else if(_endDown && scantable[0] != 8)
	{
		_endDown = false;
		_action = A_END_GAME;
		_processAction = true;
		_actionCoolDown = 0;
	}

	if(joy_vertical == 1 
			|| (scantable[0] == 128 && scantable[1] == 0 && scantable[6] == 0)
			|| scantable[2] == 32)
	{
		if(!_fastDown)
		{
			_countdown = DELAY = FAST_DOWN;
			_fastDown = true;
			_actionCoolDown = 0;
		}
	}
	else
	{
		if(_fastDown)
		{
			_fastDown = false;
			_countdown = DELAY = LAST_DELAY;
		}
	}

	if(!_do_scroll) 
	{
		return IRQ_NOT_HANDLED;
	}

	if(_toggle)
	{
		roll();
	}
	_toggle = !_toggle;

	return IRQ_NOT_HANDLED;
}

bool makeNewPiece(byte_t x, shapes_e *shape, byte_t *color)
{
	switch(rand() % 7)
	{
	case 0: *shape = S_LINE; *color = COLOR_BLUE; break;
	case 1: *shape = S_BOX; *color = COLOR_LIGHTBLUE; break;
	case 2: *shape = S_TEE; *color = COLOR_PURPLE; break;
	case 3: *shape = S_LEFT_L; *color = COLOR_GREEN; break;
	case 4: *shape = S_RIGHT_L; *color = COLOR_LIGHTGREEN; break;
	case 5: *shape = S_LEFT_S; *color = COLOR_RED; break;
	case 6: *shape = S_RIGHT_S; *color = COLOR_LIGHTRED; break;
	}

	return drawShape(x, 0, *shape, *color, O_UP, false, false, false, false);
}

void showStartMessage(void)
{
	_do_scroll = false;
	_fore_color = COLOR_BLUE;

	writestringxy(0, 1, "hit a key");
	writestringxy(0, 3, "to start game");

	_fore_color = COLOR_RED;
	writestringxy(0, 15, "code");
	writestringxy(0, 16, "..king durin");
	writestringxy(0, 18, "graphix");
	writestringxy(0, 19, "..grass");
	writestringxy(0, 20, "..warlock");
	writestringxy(0, 22, "music");
	writestringxy(0, 23, "..necropolo");
}

void main(void)
{
	static byte_t x, y, last_x, j;
	static uint_t i;
	static unsigned long score = 0;
	static bool gameOver = false;
	static bool makePiece = true;
	static shapes_e shape;
	static byte_t color;
	static orientations_e orient, old_orient;

	j = PEEK(0xD011);
	POKE(0xD011, 0);

	_fore_color = COLOR_RED;
	
	setupCharSet();
	makeSprites();

	setupLogoDecrunch();

	__asm__("SEI");
	decrunch();
	__asm__("CLI");

	fixChars();

	setup_cruncher();

	__asm__("SEI");
	decrunch();
	__asm__("CLI");

	__asm__("LDA #$00");
	__asm__("JSR $1000");

	set_irq(irq, _stack, 256);

	POKE(0xD011, j);

	_fore_color = COLOR_BLUE;

	for(i = 0; i < 33000; ++i);

	clearscreen();

	_fore_color = COLOR_GRAY3;

	for(i = 26; i<39; i += 12)
	{
		j = (i==26 ? 89 : 91);
		plotcodexy(i, 1, j);
		plotcodexy(i, 3, j);
		plotcodexy(i, 6, j);
		plotcodexy(i, 12, j);

		plotcodexy(i, 2, 81);
		plotcodexy(i, 7, 80);
		plotcodexy(i, 11, 88);
	}


	for(i = 27; i < 38; ++i)
	{
		plotcodexy(i, 6, 90);
		plotcodexy(i, 12, 90);
		plotcodexy(i, 1, 90);
		plotcodexy(i, 3, 90);
	}

	for(i = 8; i < 11; ++i)
	{
		plotcodexy(26, i, 84);
		plotcodexy(38, i, 84);
	}

	_fore_color = COLOR_RED;
	writestringxy(30, 0, "score");
	writestringxy(30, 5, "lines");

	_fore_color = COLOR_BLUE;
	writestringxy(27, 7, "single");
	writestringxy(27, 8, "double");
	writestringxy(27, 9, "triple");
	writestringxy(27, 10, "quad");
	writestringxy(27, 11, "total");

	_fore_color = COLOR_PURPLE;
	writestringxy(26, 14, "'f1' mute tune");
	writestringxy(26, 15, "'f7' new game");

	_fore_color = COLOR_RED;
	writestringxy(27, 17, "'stop' pause");

	_fore_color = COLOR_BROWN;
	x = 13;
	y = 0;

	for(i = x - 1; i < x + 11; ++i)
	{
		plotcodexy(i + 1, 24, 92);
	}

	for(i = 0; i < 24; ++i)
	{
		plotcodexy(x , i, 92);
		plotcodexy(x + 11, i, 92);
	}

	showStartMessage();

	while(_action == A_NONE);

	while(true)
	{
		_do_scroll = true;

		while(!gameOver)
		{
			if(makePiece)
			{

				score += checkLines(14, 1, 10, 22);
				writeScore(score);

				for(i = 7; i < 11; ++i)
				{				
					clearxy(33, i, 4);
					writenumberxy(37, i, lines[i - 7]);
				}
				clearxy(33, i, 4);
				writenumberxy(37, i, _totalLines);

				last_x = x = 18;
				gameOver |= makeNewPiece(x, &shape, &color);
				y = 0;
				old_orient = orient = O_UP;
				makePiece = _updateDisplay = _processAction = false;
				_action = A_NONE;
				_countdown = DELAY = LAST_DELAY;
				++score;
			}

			if(!gameOver)
			{
				if(_processAction && _action != A_NONE)
				{
					_processAction = false;
					_updateDisplay = true;
					switch(_action)
					{
					case A_END_GAME:
						gameOver = true;
						break;
					case A_PAUSE:
						_do_scroll = false;
						writestringxy(0, 23, "paused");
						_action = A_NONE;
						while(_action == A_NONE);
						_do_scroll = true;
						break;
					case A_LEFT:
						if(drawShape(x, y, shape, _fore_color, orient, false, true, false, false))
						{
							--x;
						}
						break;
					case A_RIGHT:
						if(drawShape(x, y, shape, _fore_color, orient, true, false, false, false))
						{
							++x; 
							//if(x > maxRight) x = maxRight; 
						}
						break;
					case A_TURN:
						if(((shape == S_LINE && x < 23 - 3)
							|| (shape != S_LINE && shape != S_BOX && x < 23 - 2))
							&& drawShape(x, y, shape, _fore_color, orient, true, false, false, false))
						{
							switch(orient)
							{
							case O_UP: orient = O_RIGHT; _updateDisplay = true; break;
							case O_RIGHT: orient = O_DOWN; _updateDisplay = true; break;
							case O_DOWN: orient = O_LEFT; _updateDisplay = true; break;
							case O_LEFT: orient = O_UP; _updateDisplay = true; break;
							}
						}
						_turnDown = false;
						break;
					}
					_action = A_NONE;
				}

				if(_updateDisplay)
				{
					_updateDisplay = false;
					
					makePiece = drawShape(x, y, shape, color, orient, false, false, true, false);

					if(_moveDown)
					{
						if(!makePiece) ++y;
						_moveDown = false;
						_actionCoolDown = 0;
					}
					else
					{
						makePiece = false;
					}

					drawShape(x, y, shape, color, orient, false, false, false, makePiece);
					
					if(makePiece) { _countdown = DELAY; }

					last_x = x;
					old_orient = orient;
				}				
			}
		}

		_do_scroll = false;
		
		__asm__("LDA #$01");
		__asm__("JSR $1000");

		score = 0;
		_totalLines = lines[0] = lines[1] = lines[2] = lines[3] = 0;

		for(i = 0; i < 20000u; ++i);

		showStartMessage();

		_action == A_NONE;
		
		while(_action == A_NONE);

		__asm__("LDA #$00");
		__asm__("JSR $1000");

		clearxy(0,24,10);

		for(i = 0; i < 24; ++i)
		{
			clearxy(14, i, 10);
		}

		makePiece = !(gameOver = false);
		_countdown = DELAY = LAST_DELAY = DEFAULT_DELAY;
	}
}